﻿///<copyright>
/// Copyright © 2009 Stephen Styrchak. All Rights Reserved.
///</copyright>
using System;
using System.ComponentModel;
using Ferpect.ComponentModel.Design;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
#if WINDOWS
using System.Drawing.Design;
using System.ComponentModel.Design;
#endif

namespace Ferpect.ComponentModel.Sprites
{
#if WINDOWS
    [ToolboxItemFilter("Ferpect.ComponentModel", ToolboxItemFilterType.Allow)]
    [Designer(Designers.SpriteDesigner, typeof(IDesigner))]
#endif
    public abstract class Sprite : Component
    {
        private Color color;
        private Vector2 anchor;
        private Vector2 offset;
        private Vector2 origin;
        private RelativeLocation relativeOrigin;
        private RelativeLocation anchorName;
        private float scale;

        public Sprite()
        {
            this.color = Color.White;
            this.Scale = 1f;
            this.relativeOrigin = RelativeLocation.TopLeft;
            this.Anchor = RelativeLocation.TopLeft;
            this.Visible = true;
        }

        [Browsable(false)]
        public string Name { get; set; }

        [Category("Behavior")]
        [DefaultValue(true)]
        public bool Visible
        {
            get;
            set;
        }

        [Category("Appearance")]
        [Editor(UITypeEditors.ColorEditor, UITypeEditors.UITypeEditor)]
        [DefaultValue(typeof(Color), "255,255,255,255")]
        public Color Color
        {
            get { return this.color; }
            set { this.color = value; }
        }

        [Category("Layout")]
        [ReadOnly(true)]
        public Vector2 Position
        {
            get { return this.anchor + this.offset; }
            set
            {
                this.offset = value;
                this.anchor = Vector2.Zero;
                this.Anchor = RelativeLocation.TopLeft;
                this.OnAfterPositionChanged();
            }
        }

        [Category("Layout")]
        [DefaultValue(typeof(Vector2), "0, 0")]
        public Vector2 Offset
        {
            get
            {
                return this.offset;
            }
            set
            {
                this.offset = value;
                this.OnAfterPositionChanged();
            }
        }

        [Category("Layout")]
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Vector2 Origin
        {
            get { return this.origin; }
            set
            {
                this.origin = value;
                this.OnAfterPositionChanged();
            }
        }

        [Category("Appearance")]
        [DefaultValue(false)]
        public bool FlipHorizontally { get; set; }

        [Category("Appearance")]
        [DefaultValue(false)]
        public bool FlipVertically { get; set; }

        [Category("Layout")]
        [DefaultValue(0f)]
        public float Rotation { get; set; }

        [Category("Layout")]
        [DefaultValue(1f)]
        public float Scale
        {
            get
            {
                return this.scale;
            }
            set
            {
                this.scale = value;
                this.ResetRelativeOrigin();
            }
        }

        [Category("Appearance")]
        [DefaultValue(0f)]
        public float LayerDepth { get; set; }

        [Category("Layout")]
        [DefaultValue(RelativeLocation.TopLeft)]
        [DisplayName("Origin")]
        public RelativeLocation RelativeOrigin
        {
            get
            {
                return this.relativeOrigin;
            }
            set
            {
                if (value != this.relativeOrigin)
                {
                    this.relativeOrigin = value;

                    Vector2 size = this.GetSize();
                    this.origin = Vector2.Zero;
                    switch (value)
                    {
                        case RelativeLocation.MiddleLeft:
                        case RelativeLocation.MiddleCenter:
                        case RelativeLocation.MiddleRight:
                            this.origin.Y = size.Y / 2f;
                            break;
                        case RelativeLocation.BottomLeft:
                        case RelativeLocation.BottomCenter:
                        case RelativeLocation.BottomRight:
                            this.origin.Y = size.Y;
                            break;
                    }
                    switch (value)
                    {
                        case RelativeLocation.TopCenter:
                        case RelativeLocation.MiddleCenter:
                        case RelativeLocation.BottomCenter:
                            this.origin.X = size.X / 2f;
                            break;
                        case RelativeLocation.TopRight:
                        case RelativeLocation.MiddleRight:
                        case RelativeLocation.BottomRight:
                            this.origin.X = size.X;
                            break;
                    }
                    this.OnAfterPositionChanged();
                }
            }
        }

        [Category("Layout")]
        [DefaultValue(false)]
        public bool UseTitleSafeArea
        {
            get;
            set;
        }

        [Category("Layout")]
        [DefaultValue(RelativeLocation.TopLeft)]
        [RefreshProperties(RefreshProperties.All)]
        public RelativeLocation Anchor
        {
            get
            {
                return this.anchorName;
            }
            set
            {
                //Vector2 position = this.Position;
                if (this.anchorName != value)
                {
                    this.anchorName = value;
                    IViewportService viewportService = this.GetService(typeof(IViewportService)) as IViewportService;
                    if (viewportService != null)
                    {
                        this.SetAnchorPosition(viewportService);
                    }
                }
            }
        }

        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public SpriteEffects Effects
        {
            get
            {
                SpriteEffects effects = SpriteEffects.None;
                if (this.FlipHorizontally)
                {
                    effects |= SpriteEffects.FlipHorizontally;
                }
                if (this.FlipVertically)
                {
                    effects |= SpriteEffects.FlipVertically;
                }
                return effects;
            }
        }

        protected virtual void OnAfterPositionChanged()
        {
        }

        protected virtual void OnAfterSizeChanged()
        {
        }

        /// <summary>
        /// The relative origin depends on the size of the sprite. The
        /// size generally isn't known until content is loaded, so this
        /// method should be invoked after loading content if the
        /// RelativeOrigin property is being used.
        /// </summary>
        protected void ResetRelativeOrigin()
        {
            RelativeLocation savedOrigin = this.relativeOrigin;
            this.relativeOrigin = 0;
            this.RelativeOrigin = savedOrigin;
        }

        public virtual void SetAnchorPosition(IViewportService viewportService)
        {
            if (viewportService == null) throw new ArgumentNullException("viewportService");
            this.anchor = viewportService.GetAnchorPosition(this.Anchor, this.UseTitleSafeArea);
        }

        public abstract void Draw(SpriteBatchStack spriteBatchStack);
        public abstract Vector2 GetSize();
        public abstract void LoadContent(ContentManager contentManager);
        public abstract void UnloadContent();
    }
}
